<?php
/**
 * app页面的商品管理
 * Author: lyun
 * Date: 2018/4/9
 * Created by Panxsoft.
 */

namespace App\Http\Controllers\App;

use App\Http\Controllers\ApiController;
use App\Http\Requests\App\ProductIndexRequest;
use App\Http\Requests\App\ProductTryOnRequest;
use App\Models\Panx\Brand;
use App\Models\Panx\BrandSeries;
use App\Models\Panx\Category;
use App\Models\Panx\Product;
use App\Models\Store\Store;
use App\Services\Repositories\Device\DeviceAccountRepository;
use App\Services\Repositories\Product\ProductRepository;
use App\Services\Utils\Guard;
use App\Services\Utils\PanxCollection;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Pagination\LengthAwarePaginator;

class ProductController extends ApiController
{
    const ROUTES = [
        'index'     => 'app.product.index',
        'search'    => 'app.product.search',
        'patch'     => 'app.product.patch',
        'indexPage' => 'api.app.product-page',
        'create'    => 'api.app.product.create',
    ];

    private $deviceAccount;

    private $productRepository;

    public function __construct()
    {
        $this->productRepository = new ProductRepository;
        $this->deviceAccount = Guard::deviceAccount();
    }


    /**
     * 根据分类与产品返回 品牌与对应（产品的）品牌系列
     * @test
     *
     * @param ProductIndexRequest $request
     * @return array|\Illuminate\Http\Response
     * @throws \App\Exceptions\ApiAbstractException
     */
    public function create(ProductIndexRequest $request)
    {
        $category = (new Category)->find($request->category_id, ['id']);
            $this->checkItemExist($category, trans('app.category_error'));
        $store = $this->deviceAccount->store;
        $products = $this->productRepository->appStoreProductsForCreate($store, $category->id);

        $brands = $this->assembleBrandSeriesForCreate($products);

        return $this->response(compact('brands'));
    }
    
    /**
     * 搜索商品列表
     * @deprecated
     * @test
     *
     * @param ProductIndexRequest $request
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function index(ProductIndexRequest $request)
    {
        $category = (new Category)->find($request->category_id);
            $this->checkItemExist($category, trans('app.category_error'));

        /** @var Store $store */
        $store = $this->deviceAccount->store;

        $products = $this->productRepository->appStoreProductsQuery($store, $request)->get();

        $product_groups = $this->assembleProductBrandGroups($products);

        return $this->response(compact('product_groups'));
    }


    /**
     * 搜索商品列表 - 有分页
     *
     * @test
     * @param ProductIndexRequest $request
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function indexPage(ProductIndexRequest $request)
    {
        $category = (new Category)->find($request->category_id);
            $this->checkItemExist($category, trans('app.category_error'));

        /** @var Store $store */
        $store = $this->deviceAccount->store;

        /** @var LengthAwarePaginator $products */
        $products = $this->productRepository->appStoreProductsQuery($store, $request)
            ->paginate($request->per_page ?: 20);

        $products->transform([$this, 'transformProduct']);

        return $this->response(compact('products'));
    }

    /**
     * 从商品spu_code，查找商品详情
     * @test
     *
     * @param $spu_code
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function searchProductSpuCode(string $spu_code)
    {
        $store = Guard::deviceAccount()->store;

        $product = (new Product)
            ->filterStoreMixCustomer($store)
            // 注意这里可能因为店铺下架了商家商品，但商家设置了 banner 跳转，导致使用 spu_code 查不到对应的商品
            ->filterStorePivotCustomerProductOnShelf($store->id)
            ->filterOnShelf()
            ->where('spu_code', $spu_code)
            ->first();

        $this->checkItemExist($product, trans('app.spu_code'));

        $product = $this->transformProduct($product);

        return $this->response(compact('product'));
    }

    /**
     * 增加试戴次数
     * @test
     *
     * @param ProductTryOnRequest $request
     * @param DeviceAccountRepository $deviceAccountRepository
     * @param $id
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function patchTryOn(ProductTryOnRequest $request, DeviceAccountRepository $deviceAccountRepository, $id)
    {
        /** @var Product $product */
        $product = Product::find($id);
        $this->checkItemExist($product, trans('app.product_error'));

        $increment = ((int)$request->count) ?: 1;

        $deviceAccountRepository->recordsProductTryOn($product, $increment);

        return $this->response(compact('increment', 'product'));
    }

    /**
     * @param Collection $products
     * @return Collection
     */
    private function assembleProductBrandGroups(Collection $products)
    {
        $prod_brand_groups = $products
            ->groupBy(function (Product $product) {
                return $product->brand->name ?? trans('app.remark_error');
            })
            ->transform(function (Collection $products, string $brand_name) {
                $this->transformProducts($products);

                $brand = $products->first()->brand;

                $this->appendBrandSeriesForProducts($brand, $products);

                return [
                    'brand_name' => $brand_name,
                    'brand'      => $brand,
                    'products'   => $products
                ];
            })
            ->values();

        return $prod_brand_groups;
    }

    /**
     * @param Collection $products
     */
    private function transformProducts(Collection $products): void
    {
        $products->each(function (Product $product) {
            $this->transformProduct($product);
        });
    }

    /**
     * callback must be public
     *
     * @param Product $product
     *
     * @return Product
     */
    public function transformProduct(Product $product): Product
    {
        $product->append([
            'thumb_url', 'color_thumb',
            'asset_xml', 'asset_version',
            'tag_names', 'brand_name', 'brand_series_name',
            'category_name', 'link_product', 'asset_code'
        ]);

        $product->addHidden([
            'brand', 'thumbImage', 'asset', 'productTags', 'productUrlType', 'colorThumbImage', 'brandSeries'
        ]);

        if ($product->link_product) { // #885 convert url redirect and log visit
            $product->link_product->convert_url = $this->productRepository->getAppConvertUrl(
                $product->id, $this->deviceAccount->id
            );
        }

        return $product;
    }


    /**
     * 判断当前products中是否有其他系列中的商品
     *
     * @param array $brand_series_ids
     *
     * @return bool
     */
    private function hasNoBrandSeriesId(array $brand_series_ids): bool
    {
        return in_array(BrandSeries::ID_OTHER, $brand_series_ids);
    }

    /**
     * @param Brand $brand
     * @param Collection $products
     */
    private function appendBrandSeriesForProducts(Brand $brand, Collection $products): void
    {
        $brand_series_ids = $products
            ->pluck('brand_series_id')
            ->unique()->values()->all();

        $brand->brand_series = $brand->brandSeriesBySortLatest
            ->whereIn('id', $brand_series_ids)
            ->values();

        if ($this->hasNoBrandSeriesId($brand_series_ids)) {
            $this->appendBrandSeriesOther($brand);
        }

        $brand->unsetRelation('brandSeriesBySortLatest');
    }

    /**
     * @param $products
     *
     * @return Collection
     */
    private function assembleBrandSeriesForCreate(Collection $products): Collection
    {
        $prod_brand_ids = $products
            ->pluck('brand_id')
            ->unique()->values();

        $brands = (new Brand)->whereKey($prod_brand_ids)
            ->orderBy('sort', 'asc')
            ->with('brandSeriesBySortLatest')
            ->get();

        $prods_group_by_brand = $products->groupBy('brand_id');

        $brands->each(function (Brand $brand) use ($prods_group_by_brand) {
            /** @var Collection $products */
            $products = $prods_group_by_brand[$brand->id];

            $this->appendBrandSeriesForProducts($brand, $products);
        });

        return $brands;
    }

    /**
     * App 需要向前兼容显示一个默认的系列
     *
     * @param Brand $brand
     */
    private function appendBrandSeriesOther(Brand $brand): void
    {
        $other_series = BrandSeries::newOther();
        $other_series->image = $brand->logo;
        $brand->brand_series->push($other_series);
    }
}
